﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using MegavaniaX.Rendering;
using MegavaniaX;
using MegavaniaX.Messaging.Events;

namespace MegavaniaX.World
{
    /// <summary>
    /// Map manager class, handles loading and saving of different maps.
    /// </summary>
    public static class MapManager
    {
        public static Map CurrentMap;       
        public static MegavaniaCamera Camera;
        public static bool UpdateActors = true;

        /// <summary>
        /// Updates the world.
        /// </summary>
        /// <param name="elapsedTime"></param>
        public static void Update(TimeSpan elapsedTime)
        {
            CurrentMap.Update(elapsedTime, Camera, UpdateActors);
        }

        /// <summary>
        /// Draws the world.
        /// </summary>
        /// <param name="spriteBatch"></param>
        public static void Draw(SpriteBatch spriteBatch, MegavaniaCamera camera)
        {
            CurrentMap.Draw(spriteBatch, camera);
            DrawActors(spriteBatch, camera);
        }


        public static void OnScreenTransistionStart(IMegavaniaEvent ievt)
        {
            var evt = (ScreenTransistionEvent)ievt;
            var newscreen = CurrentMap.GetAdjacentScreen(evt.Side, evt.PlayerX, evt.PlayerY);
            if (newscreen == null)
            {
                MegavaniaX.logger.Warn("Tried to transistion to an empty screen");
                return;
            }


            // turn off actor updates
            UpdateActors = false;

            var player = CurrentMap.Actors.FirstOrDefault(ac => ac.ActorID == "player");
            // if player is null it'll throw an exception, but that should never ever happen so that's fine


            if (evt.Side == 'R' || evt.Side == 'L')
            {
                 // adjust camera y position, if needed
                if (Camera.gameY < newscreen.TopBound)
                    Camera.gameY = newscreen.TopBound;
                if (Camera.gameY + 256.0f > newscreen.BottomBound)
                    Camera.gameY = newscreen.BottomBound - 256.0f;

                if (evt.Side == 'R')
                {
                    player.position.X = newscreen.LeftBound + 16.0f + (4.0f / 3.0f) * player.Width;
                    // Camera.gameX = newscreen.LeftBound;
                    Camera.StartTransition(newscreen.LeftBound, Camera.gameY, new Vector2(MegavaniaCamera.TransitionSpeed, 0.0f));
                    // Camera.gameY = newscreen.TopBound + evt.ToY * Screen.ScreenHeight * 16.0f;
                }
                else if (evt.Side == 'L')
                {
                    player.position.X = newscreen.RightBound - 16.0f - (4.0f / 3.0f) * player.Width;
                    // Camera.gameX = newscreen.RightBound - Screen.ScreenWidth * 16.0f;
                    Camera.StartTransition(newscreen.RightBound - Screen.ScreenWidth * 16.0f, Camera.gameY, new Vector2(-MegavaniaCamera.TransitionSpeed, 0.0f));
                    // Camera.gameY = newscreen.TopBound + evt.ToY * Screen.ScreenHeight * 16.0f;
                }

               

            }
            else if (evt.Side == 'T' || evt.Side == 'B')
            {
                // adjust camera x pos if needed
                if (Camera.gameX < newscreen.LeftBound)
                    Camera.gameX = newscreen.LeftBound;
                if (Camera.gameX + 256.0f > newscreen.RightBound)
                    Camera.gameY = newscreen.RightBound - 256.0f;

                if (evt.Side == 'T')
                {
                    player.position.Y = newscreen.BottomBound - 16.0f - (4.0f / 3.0f) * player.Height;
                    // Camera.gameX = newscreen.LeftBound + evt.ToX * Screen.ScreenWidth * 16.0f;
                    // Camera.gameY = newscreen.BottomBound - Screen.ScreenHeight * 16.0f;                    
                    Camera.StartTransition(Camera.gameX, newscreen.BottomBound - Screen.ScreenHeight * 16.0f, new Vector2(0.0f, -MegavaniaCamera.TransitionSpeed));
                }
                else if (evt.Side == 'B')
                {
                    player.position.Y = newscreen.TopBound + 16.0f + (4.0f / 3.0f) * player.Height;
                    // Camera.gameX = newscreen.LeftBound + evt.ToX * Screen.ScreenWidth * 16.0f;
                    //Camera.gameY = newscreen.TopBound;
                    Camera.StartTransition(Camera.gameX, newscreen.TopBound, new Vector2(0.0f, MegavaniaCamera.TransitionSpeed));
                }
            }
            else
            {
                throw new MegavaniaException(string.Format("Unknown side {0}", evt.Side));
            }

            CurrentMap.NextScreen = newscreen;
        }

        public static void OnScreenTransistionEnd(IMegavaniaEvent ievt)
        {
            var evt = (ScreenTransistionEvent)ievt;
            // turn on actor updating
            UpdateActors = true;
            var player = CurrentMap.Actors.FirstOrDefault(ac => ac.ActorID == "player");
            CurrentMap.CurrentScreen = CurrentMap.NextScreen;
            CurrentMap.NextScreen = null;
        }




        public static void DrawActors(SpriteBatch batch, MegavaniaCamera camera)
        {
            foreach (var entry in CurrentMap.Actors)
            {
                if (entry.CurrentSprite().Texture != null && camera.IsInViewport(entry.position.X, entry.position.Y, entry.Height, entry.Width))
                {
                    // Vector2 offsetCoordinates = entry.Value.position + new Vector2(entry.Value.CurrentSprite().offsetX, entry.Value.CurrentSprite().offsetY);
                    Vector2 screenCoordinates = camera.GameCoordinatesToScreenCoordinates(entry.position);
                    if (entry.IsFacingLeft) // have to do some offsetting here
                    {
                        screenCoordinates.X += entry.Width - entry.CurrentSprite().width;
                    }
                    screenCoordinates.X += (entry.CurrentSprite().offsetX * camera.ZoomFactor);
                    screenCoordinates.Y += (entry.CurrentSprite().offsetY * camera.ZoomFactor);
                    SpriteEffects fx = entry.IsFacingLeft ? SpriteEffects.FlipHorizontally : SpriteEffects.None;

                    batch.Draw(entry.CurrentSprite().Texture, screenCoordinates, null, Color.White, 0.0f, Vector2.Zero, camera.ZoomFactor, fx, 0);
                }
            }
        }


        public static void StartClimbing(IMegavaniaEvent evt)
        {
            var player = CurrentMap.Actors.Single(x => x.ActorID == "player") as Player;
            var cevt = (ClimbEvent)(evt);
            cevt.Screen = CurrentMap.CurrentScreen;
            player.HandleEvent(cevt);
        }


        public static void OnSpawnActor(IMegavaniaEvent evt)
        {
            var spawnActorEvent = (SpawnActorEvent)evt;
            var newActor = GetActorFromString(spawnActorEvent.ActorType);

            // set position
            newActor.position.X = spawnActorEvent.PositionX;
            newActor.position.Y = spawnActorEvent.PositionY;
            // set velo
            newActor.velocity.X = spawnActorEvent.VelocityX;
            newActor.velocity.Y = spawnActorEvent.VelocityY;
            // set accel
            newActor.acceleration.X = spawnActorEvent.AccelerationX;
            newActor.acceleration.Y = spawnActorEvent.AccelerationY;

            CurrentMap.Actors.Add(newActor);
        }

        public static void OnDespawnActor(IMegavaniaEvent evt)
        {
            var act = (DespawnActorEvent)evt;


            var actorToDespawn = act.Actor;
            if (actorToDespawn == null)
            {
                MegavaniaX.logger.Warn("Received request to despawn actor without a valid \"actor\" parameter");
                return;
            }

            CurrentMap.Actors.Remove(actorToDespawn);

        }

        private static Actor GetActorFromString(string actorType)
        {
            if (actorType == "bullet")
            {
                return new Bullet("tempID");
            }

            throw new MegavaniaException(string.Format("Could not find actor type matching {0}", actorType));
        }

    }
}
